#include "../s21_decimal.h"

int s21_add(s21_decimal value_1, s21_decimal value_2, s21_decimal *result) {
  if (s21_is_invalid(&value_1) || s21_is_invalid(&value_2) || result == NULL) {
    return NAN_ERROR;
  }

  int status = OK;
  int sign_1 = s21_get_sign(&value_1);
  int sign_2 = s21_get_sign(&value_2);

  if (sign_1 == POSITIVE && sign_2 == NEGATIVE) {
    s21_set_sign(&value_2, POSITIVE);
    status = s21_sub(value_1, value_2, result);
  } else if (sign_1 == NEGATIVE && sign_2 == POSITIVE) {
    s21_set_sign(&value_1, POSITIVE);
    status = s21_sub(value_2, value_1, result);
  } else {  // sing_1 == sing_2 -> result_sing
    s21_long_decimal first = INIT_ZERO_S21_LONG_DECIMAL;
    s21_extend(&value_1, &first);
    s21_long_decimal second = INIT_ZERO_S21_LONG_DECIMAL;
    s21_extend(&value_2, &second);
    s21_equalize_long_scale(&first, &second);
    s21_long_decimal sum = INIT_ZERO_S21_LONG_DECIMAL;
    s21_add_long_bits_ignore_scale(&first, &second, &sum);
    s21_set_long_scale(&sum, s21_get_long_scale(&first));
    s21_set_long_sign(&sum, sign_1);
    status = s21_reduce(&sum, result);
  }

  return status;
}

int s21_sub(s21_decimal value_1, s21_decimal value_2, s21_decimal *result) {
  if (s21_is_invalid(&value_1) || s21_is_invalid(&value_2) || result == NULL) {
    return NAN_ERROR;
  }

  int status = OK;

  if (s21_get_sign(&value_1) != s21_get_sign(&value_2)) {
    s21_inverse_sign(&value_2);
    status = s21_add(value_1, value_2, result);
  } else {
    s21_long_decimal first = INIT_ZERO_S21_LONG_DECIMAL;
    s21_extend(&value_1, &first);
    s21_long_decimal second = INIT_ZERO_S21_LONG_DECIMAL;
    s21_extend(&value_2, &second);
    s21_equalize_long_scale(&first, &second);
    s21_long_decimal difference = INIT_ZERO_S21_LONG_DECIMAL;
    s21_set_long_scale(&difference, s21_get_long_scale(&first));
    s21_set_long_sign(&difference, s21_get_long_sign(&first));

    if (s21_compare_only_long_bits(&first, &second) >= 0) {
      s21_sub_long_bits_ignore_scale(&first, &second, &difference);
    } else {
      s21_sub_long_bits_ignore_scale(&second, &first, &difference);
      s21_inverse_long_sign(&difference);
    }

    status = s21_reduce(&difference, result);
  }

  return status;
}

void s21_mul_long_by_ten(s21_long_decimal *value) {
  s21_long_decimal mul_by_2_value = INIT_ZERO_S21_LONG_DECIMAL;
  s21_copy_long(value, &mul_by_2_value);
  s21_shift_long_bits_to_left(&mul_by_2_value, 1);
  s21_long_decimal mul_by_8_value = INIT_ZERO_S21_LONG_DECIMAL;
  s21_copy_long(value, &mul_by_8_value);
  s21_shift_long_bits_to_left(&mul_by_8_value, 3);
  s21_add_long_bits_ignore_scale(&mul_by_2_value, &mul_by_8_value, value);
}

int s21_mul(s21_decimal value_1, s21_decimal value_2, s21_decimal *result) {
  if (s21_is_invalid(&value_1) || s21_is_invalid(&value_2) || result == NULL) {
    return NAN_ERROR;
  }

  s21_long_decimal first = INIT_ZERO_S21_LONG_DECIMAL;
  s21_extend(&value_1, &first);
  s21_long_decimal second = INIT_ZERO_S21_LONG_DECIMAL;
  s21_extend(&value_2, &second);
  s21_long_decimal composition = INIT_ZERO_S21_LONG_DECIMAL;
  s21_long_decimal temp_sum = INIT_ZERO_S21_LONG_DECIMAL;
  int top_bit_index = s21_get_top_long_bit_index(&second);

  for (int i = 0, shift = 0; i <= top_bit_index; i++) {
    if (s21_get_long_bit(&second, i) == 1) {
      s21_shift_long_bits_to_left(&first, shift);
      s21_add_long_bits_ignore_scale(&composition, &first, &temp_sum);
      s21_copy_long(&temp_sum, &composition);
      shift = 0;
    }

    shift++;
  }

  s21_set_long_scale(&composition,
                     s21_get_long_scale(&first) + s21_get_long_scale(&second));
  s21_set_long_sign(&composition,
                    s21_get_long_sign(&first) ^ s21_get_long_sign(&second));

  return s21_reduce(&composition, result);
}

int s21_div_long_by_ten(s21_long_decimal *value) {
  s21_long_decimal value_copy = INIT_ZERO_S21_LONG_DECIMAL;
  s21_copy_long(value, &value_copy);
  s21_long_decimal ten = INIT_ZERO_S21_LONG_DECIMAL;
  ten.bits[0] = 10;
  s21_long_decimal remainder = INIT_ZERO_S21_LONG_DECIMAL;
  s21_integer_div_long_ignore_scale(&value_copy, &ten, value, &remainder);

  return remainder.bits[0];
}

int s21_div(s21_decimal value_1, s21_decimal value_2, s21_decimal *result) {
  if (s21_is_invalid(&value_1) || s21_is_invalid(&value_2) || result == NULL) {
    return NAN_ERROR;
  }

  s21_long_decimal value = INIT_ZERO_S21_LONG_DECIMAL;
  s21_extend(&value_1, &value);
  s21_long_decimal divider = INIT_ZERO_S21_LONG_DECIMAL;
  s21_extend(&value_2, &divider);
  s21_long_decimal quotient = INIT_ZERO_S21_LONG_DECIMAL;
  int quotient_scale =
      s21_get_long_scale(&value) - s21_get_long_scale(&divider);

  for (; quotient_scale < 0; quotient_scale++) {
    s21_mul_long_by_ten(&value);
  }  // quotient_scale == 0 or more

  s21_long_decimal remainder = INIT_ZERO_S21_LONG_DECIMAL;
  int status = s21_integer_div_long_ignore_scale(&value, &divider, &quotient,
                                                 &remainder);

  if (status == OK) {
    while (s21_is_not_long_zero(&remainder) && quotient_scale < 28) {
      s21_mul_long_by_ten(&remainder);
      s21_long_decimal next_quotient = INIT_ZERO_S21_LONG_DECIMAL;
      s21_long_decimal buffer = INIT_ZERO_S21_LONG_DECIMAL;
      s21_copy_long(&remainder, &buffer);
      s21_integer_div_long_ignore_scale(&buffer, &divider, &next_quotient,
                                        &remainder);
      s21_long_decimal quotient_sum = INIT_ZERO_S21_LONG_DECIMAL;
      s21_mul_long_by_ten(&quotient);
      s21_add_long_bits_ignore_scale(&quotient, &next_quotient, &quotient_sum);
      s21_copy_long(&quotient_sum, &quotient);
      quotient_scale++;
    }

    s21_set_long_scale(&quotient, quotient_scale);
    s21_set_long_sign(&quotient,
                      s21_get_long_sign(&value) ^ s21_get_long_sign(&divider));
    status = s21_reduce(&quotient, result);
  }

  return status;
}
